from flask import Flask, render_template_string, Response, request, jsonify

import cv2
import numpy as np
import threading
import json
import os
import webbrowser
import signal 
import time 
from driver.orbbec_img import *
import os

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
CONFIG_FILE = os.path.join(BASE_DIR, "..", "model", "hsv_config.json")
CONFIG_FILE = os.path.abspath(CONFIG_FILE)


app = Flask(__name__)

hsv_configs = {}
current_color_name = "default" 
lock = threading.Lock()
shutdown_event = threading.Event()
shutdown_server_attempted = False

# --- 配置加载与保存 (移除删除相关的逻辑，但核心不变) ---
def load_config():
    global hsv_configs, current_color_name
    initial_default_name = "default" 

    if os.path.exists(CONFIG_FILE):
        try:
            with open(CONFIG_FILE, 'r') as f:
                content = f.read()
                if not content.strip(): 
                    print(f"配置文件 '{CONFIG_FILE}' 为空。将使用默认配置。")
                    _initialize_default_config(default_name=initial_default_name)
                    return 
                loaded_data = json.loads(content) 
            
            with lock:
                temp_hsv_configs = loaded_data 
                hsv_configs.clear()
                valid_configs_loaded = False
                for color_name, params in temp_hsv_configs.items():
                    if isinstance(params, dict) and "lower" in params and "upper" in params:
                        try:
                            hsv_configs[color_name] = {
                                "lower": np.array(params["lower"], dtype=int),
                                "upper": np.array(params["upper"], dtype=int)
                            }
                            valid_configs_loaded = True
                        except (ValueError, TypeError):
                            print(f"警告: 颜色 '{color_name}' 的参数格式无效，已跳过。")
                    else:
                        print(f"警告: 颜色 '{color_name}' 的条目结构不正确，已跳过。")
                
                if valid_configs_loaded and hsv_configs:
                    current_color_name = list(hsv_configs.keys())[0]
                else:
                    print("配置文件为空或所有条目均无效，初始化默认配置。")
                    _initialize_default_config(default_name=initial_default_name)
            
            if not hsv_configs: 
                 _initialize_default_config(default_name=initial_default_name)
            print(f"配置加载完成。当前颜色: {current_color_name}, 配置数量: {len(hsv_configs)}")
        except json.JSONDecodeError:
            print(f"错误: 配置文件 '{CONFIG_FILE}' 格式无效。将使用默认配置。")
            _initialize_default_config(default_name=initial_default_name)
        except Exception as e:
            print(f"加载配置时发生未知错误: {e}。将使用默认配置。")
            _initialize_default_config(default_name=initial_default_name)
    else:
        print(f"配置文件 '{CONFIG_FILE}' 未找到。将创建并使用默认配置。")
        _initialize_default_config(default_name=initial_default_name)
        save_config() 

def _initialize_default_config(default_name="default"):
    
    global hsv_configs, current_color_name
    with lock:
        current_color_name = default_name
        hsv_configs.clear()
        hsv_configs[current_color_name] = {
            "lower": np.array([0, 100, 100]),
            "upper": np.array([10, 255, 255]),
        }
    print(f"已初始化默认配置: {current_color_name}")

def save_config(): # (与之前相同，确保保存逻辑正确)
    global hsv_configs 
    config_path = CONFIG_FILE
    config_dir = os.path.dirname(config_path)
    if config_dir and not os.path.exists(config_dir):
        try: os.makedirs(config_dir)
        except OSError as e: 
            print(f"创建目录 {config_dir} 失败: {e}")
            return 
    data_to_save = {}

    with lock: 

        if not hsv_configs:
            print("警告: hsv_configs 为空,将保存一个空的JSON对象。")
        for color, params in hsv_configs.items(): 
            if isinstance(params, dict) and "lower" in params and "upper" in params and \
               isinstance(params["lower"], np.ndarray) and params["lower"].shape == (3,) and \
               isinstance(params["upper"], np.ndarray) and params["upper"].shape == (3,):
                 data_to_save[color] = {
                    "lower": params["lower"].tolist(),
                    "upper": params["upper"].tolist()
                }
            else:
                print(f"警告: 颜色 '{color}' 的参数结构不正确，将不会被保存。Params: {params}")

    try:
        with open(config_path, 'w') as f:
            json.dump(data_to_save, f, indent=4)
        print(f"配置已成功保存到 {config_path}。保存的内容 keys: {list(data_to_save.keys())}")
    except Exception as e:
        print(f"保存配置文件时发生错误: {e}")

# Camera class (与之前相同，确保 get_frame 逻辑正确)
class Camera:
    def __init__(self, src=0):
        self.cap = OrbbecCamera(color=True, depth=False)
        self.cap.start()
        self.is_valid = True
        self.frame_width_orig = 640 
        self.frame_height_orig = 480 
        self.actual_width = 640
        self.actual_height = 480
        self.display_frame_height = 240 
        self.running = True
        
    def get_frame(self):
        if not self.is_valid or not self.running:
            error_display_width = int(self.display_frame_height * (self.actual_width / self.actual_height) if self.actual_height > 0 else 320)
            error_frame_combined = np.zeros((self.display_frame_height * 2 + 30, error_display_width, 3), dtype=np.uint8)
            cv2.putText(error_frame_combined, "Camera Error or Stopped", (10, self.display_frame_height), 
                        cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0,0,255), 2)
            _, jpeg = cv2.imencode('.jpg', error_frame_combined)
            return jpeg.tobytes()

        frame_orig, _ = self.cap.read_frames()
        if frame_orig is not None:
            target_w = int(self.display_frame_height * (self.actual_width / self.actual_height) if self.actual_height > 0 else self.actual_width)
            # h, w = frame_orig.shape[:2]
            # roi_x, roi_y, roi_w, roi_h = w // 4, h // 6, w // 2, h //2  # 居中区域
            # roi = frame_orig[roi_y:roi_y + roi_h, roi_x:roi_x + roi_w]
            # frame_orig = roi  # 替换为 ROI 区域进行后续处理

            frame_display = cv2.resize(frame_orig, (target_w, self.display_frame_height))
            lower, upper = np.array([0,0,0]), np.array([179,255,255]) 
            active_color_display_name = "N/A"
            with lock:
                active_color_display_name = current_color_name 
                if active_color_display_name in hsv_configs and \
                   hsv_configs.get(active_color_display_name) and \
                   isinstance(hsv_configs[active_color_display_name].get("lower"), np.ndarray) and \
                   isinstance(hsv_configs[active_color_display_name].get("upper"), np.ndarray):
                    params = hsv_configs[active_color_display_name]
                    lower = params["lower"]
                    upper = params["upper"]
                else: 
                    if hsv_configs:
                        for name, cfg_params in hsv_configs.items(): 
                            if cfg_params and \
                               isinstance(cfg_params.get("lower"), np.ndarray) and \
                               isinstance(cfg_params.get("upper"), np.ndarray):
                                params = cfg_params; lower = params["lower"]; upper = params["upper"]
                                active_color_display_name = name; break 
            
            hsv = cv2.cvtColor(frame_orig, cv2.COLOR_BGR2HSV)
            mask = cv2.inRange(hsv, lower, upper)
            mask_bgr_display = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
            mask_bgr_display = cv2.resize(mask_bgr_display, (target_w, self.display_frame_height))
            combined_vertical = np.vstack([frame_display, mask_bgr_display])
            text_area_height = 30
            final_image_width = combined_vertical.shape[1]
            final_image_height = combined_vertical.shape[0] + text_area_height
            final_display_canvas = np.full((final_image_height, final_image_width, 3), (50,50,50), dtype=np.uint8)
            final_display_canvas[0:combined_vertical.shape[0], 0:final_image_width] = combined_vertical
            text_info = f"{active_color_display_name} H:{lower[0]}-{upper[0]} S:{lower[1]}-{upper[1]} V:{lower[2]}-{upper[2]}"
            if not (isinstance(lower, np.ndarray) and lower.shape == (3,) and isinstance(upper, np.ndarray) and upper.shape == (3,)):
                text_info = f"{active_color_display_name} Error: Invalid HSV params"
            cv2.putText(final_display_canvas, text_info, (5, combined_vertical.shape[0] + 20), 
                        cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 255, 200), 1)
            _, jpeg = cv2.imencode('.jpg', final_display_canvas)
            return jpeg.tobytes()
        return None

    def release(self): # (与之前相同)
        print("释放摄像头..."); self.running = False
        self.cap.stop()
        print("摄像头已释放。")


# --- 初始化 ---
load_config()
camera = Camera()

def gen_frames(): # (与之前相同)
    while camera.running and not shutdown_event.is_set():
        frame = camera.get_frame()
        if frame: yield (b'--frame\r\nContent-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
        else: time.sleep(0.01)

@app.route('/')
def index():
    global current_color_name, hsv_configs 
    with lock:
        active_color_name = current_color_name 
        if not (active_color_name in hsv_configs and 
                hsv_configs.get(active_color_name) and 
                isinstance(hsv_configs[active_color_name].get("lower"), np.ndarray) and 
                isinstance(hsv_configs[active_color_name].get("upper"), np.ndarray)):
            found_valid = False
            if hsv_configs: 
                for name, cfg_params in hsv_configs.items():
                    if cfg_params and isinstance(cfg_params.get("lower"),np.ndarray) and isinstance(cfg_params.get("upper"),np.ndarray):
                        active_color_name = name; current_color_name = active_color_name 
                        found_valid = True; break
            if not found_valid: _initialize_default_config(); active_color_name = current_color_name 
        params = hsv_configs[active_color_name] 
        lower_val, upper_val = params["lower"], params["upper"]
        color_names_list = list(hsv_configs.keys())
    if not isinstance(lower_val,np.ndarray) or lower_val.shape!=(3,): lower_val=np.array([0,0,0])
    if not isinstance(upper_val,np.ndarray) or upper_val.shape!=(3,): upper_val=np.array([179,255,255])

    return render_template_string('''
    <!DOCTYPE html>
    <html>
    <head>
        <title>HSV调节器 - 紧凑布局</title>
        <style>
            body { 
                font-family: Arial, sans-serif; 
                margin: 0; /* 移除所有外边距 */
                padding: 5px; /* 仅保留少量内边距 */
                background: #e0e0e0; /* 稍浅的背景 */
                color: #333; 
                display: flex; 
                height: calc(100vh - 10px); /* 视口高度减去少量padding */
                box-sizing: border-box;
            }
            .media-column {
                flex: 0 0 55%; /* 媒体列宽度，可以调整 */
                /* padding-right: 5px; /* 减小列间距 */
                display: flex;
                flex-direction: column;
                justify-content: flex-start; 
                overflow-y: auto; 
                max-height: 100%;
            }
            .media-column h1 { margin: 5px 0 10px 0; text-align: center; font-size: 1.3em; }
            .video-feed-container {
                width: 100%;
                max-height: calc(100% - 40px); /* 媒体列减去标题高度 */
                margin-bottom: 5px; 
            }
            .video-feed-container img {
                display: block; 
                width: 100%;   
                height: auto;  
                max-height: 100%; 
                border: 1px solid #bbb; /* 浅色边框 */
                border-radius: 3px;
                object-fit: contain; 
            }
            .controls-column {
                flex: 0 0 44%; /* 控制列宽度，与媒体列加起来略小于100%以防万一 */
                margin-left: 5px; /* 控制面板和媒体列的间距 */
                background: #f8f8f8; /* 控制面板背景色 */
                padding: 10px;
                border-radius: 6px;
                box-shadow: 0 1px 5px rgba(0,0,0,0.1);
                overflow-y: auto; 
                max-height: 100%; 
            }
            .controls-column h2 { margin-top: 0; color: #444; font-size: 1.1em; }
            .color-management, .slider-group { 
                margin-bottom: 12px; padding-bottom: 8px; 
                border-bottom: 1px solid #ddd; 
            }
            .color-management:last-child, .slider-group:last-child { border-bottom: none; }
            label, .slider-label { display: block; font-weight: bold; margin-bottom: 6px; font-size: 0.85em;}
            .slider-container { display: flex; align-items: center; margin-bottom: 3px; }
            input[type="range"] { flex-grow: 1; margin: 0 4px; height: 8px; }
            .value-display { width: 30px; text-align: center; font-family: monospace; background: #eee; padding: 3px; border-radius: 2px; font-size: 0.85em; }
            #color_select { width: calc(100% - 10px); margin-bottom: 8px; padding: 6px; font-size: 0.9em;}
            .button-group { margin-top: 15px; text-align: center; }
            .button {
                background: #007bff; color: white; padding: 7px 10px; border: none;
                border-radius: 3px; cursor: pointer; margin: 4px; 
                font-size: 0.85em; transition: background-color 0.2s;
            }
            .button:hover { background: #0056b3; }
            .save-btn { background: #28a745; } .save-btn:hover { background: #1e7e34; }
            /* 删除按钮被移除 */
            .exit-btn { background: #6c757d; } .exit-btn:hover { background: #545b62; }
        </style>
        <script>
            let currentColor = ""; 

            function updateSliderValues() { /* ... (与之前相同) ... */ }
            function updateParams() { /* ... (与之前相同) ... */ }
            function saveAllConfigs() { /* ... (与之前相同) ... */ }
            function selectColor() { /* ... (与之前相同) ... */ }
            function loadSpecificColor(colorName, isNew = false) { /* ... (与之前相同, 确保 console logs) ... */ }
            // deleteCurrentColor 函数已移除
            function shutdownServer() { /* ... (与之前相同) ... */ }
            window.onload = function() { /* ... (与之前相同, 确保 console logs) ... */ };

            // --- 粘贴之前版本中完整的JS函数到这里 ---
            // (确保 updateSliderValues, updateParams, saveAllConfigs, 
            //  selectColor, loadSpecificColor, shutdownServer, window.onload 都在)
            // 注意：deleteCurrentColor 函数及其调用已被移除

            // 粘贴区开始
            function updateSliderValues() { 
                document.querySelectorAll('.value-display').forEach(elem => {
                    const id = elem.id.replace('_value', '');
                    const slider = document.getElementById(id);
                    if (slider) elem.textContent = slider.value;
                });
            }

            function updateParams() { 
                updateSliderValues();
                const params = {
                    color_name: currentColor,
                    h_min: parseInt(document.getElementById('h_min').value),
                    h_max: parseInt(document.getElementById('h_max').value),
                    s_min: parseInt(document.getElementById('s_min').value),
                    s_max: parseInt(document.getElementById('s_max').value),
                    v_min: parseInt(document.getElementById('v_min').value),
                    v_max: parseInt(document.getElementById('v_max').value)
                };
                fetch('/set_params', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify(params)
                }).then(response => {
                    if (!response.ok) {
                        response.json().then(err => console.error("Failed to set params:", err.message || response.status))
                                      .catch(()=> console.error("Failed to set params and parse error:", response.status));
                    }
                });
            }
            
            function saveAllConfigs() { 
                fetch('/save_config_trigger') 
                    .then(response => response.json()) 
                    .then(data => {
                        if (data.success) alert(data.message || '所有颜色配置已保存！');
                        else alert('保存失败: ' + (data.message || '未知错误'));
                    })
                    .catch(err => alert('保存请求失败: ' + err));
            }

            function selectColor() {
                const selectedValue = document.getElementById('color_select').value;
                console.log("selectColor called, selectedValue:", selectedValue);
                if (selectedValue === "_new_color_") {
                    const newColorName = prompt("请输入新颜色的名称:", "custom_color_" + Date.now()%1000);
                    if (newColorName && newColorName.trim() !== "") {
                        const finalNewColorName = newColorName.trim().replace(/\\s+/g, '_');
                        const selectElem = document.getElementById('color_select');
                        if (Array.from(selectElem.options).some(opt => opt.value === finalNewColorName)) {
                            alert(`颜色 "${finalNewColorName}" 已存在。请选择它或输入不同名称。`);
                            selectElem.value = currentColor; 
                            return;
                        }
                        loadSpecificColor(finalNewColorName, true); 
                    } else { 
                        document.getElementById('color_select').value = currentColor; 
                    }
                } else {
                    loadSpecificColor(selectedValue);
                }
            }

            function loadSpecificColor(colorName, isNew = false) {
                console.log(`Frontend: loadSpecificColor for '${colorName}', isNew=${isNew}`);
                if (colorName === "_new_color_") { console.warn("Attempted to load _new_color_."); return; }
                if (!colorName || colorName.trim() === "") { console.error("Attempted to load empty color name."); return; }

                fetch(`/get_color_params/${colorName}?is_new=${isNew}`)
                    .then(response => { 
                        if (!response.ok) { 
                            return response.json().then(errData => { 
                                throw new Error(errData.message || `服务器错误: ${response.status} fetching ${colorName}`);
                            }).catch(() => { 
                                throw new Error(`服务器错误: ${response.status} fetching ${color_name}, no details.`);
                            });
                        }
                        return response.json();
                    })
                    .then(data => { 
                        console.log(`Frontend: Received params for '${colorName}':`, data);
                        if(data.error) { alert(data.error); document.getElementById('color_select').value = currentColor; return; }
                        if (data.message && data.message.includes("切换到")) { alert(data.message); }
                        const actualColorLoaded = data.actual_color_loaded || colorName; 

                        document.getElementById('h_min').value = data.lower[0];
                        document.getElementById('h_max').value = data.upper[0];
                        document.getElementById('s_min').value = data.lower[1];
                        document.getElementById('s_max').value = data.upper[1];
                        document.getElementById('v_min').value = data.lower[2];
                        document.getElementById('v_max').value = data.upper[2];
                        updateSliderValues(); 
                        
                        const selectElem = document.getElementById('color_select');
                        if (isNew && !Array.from(selectElem.options).some(opt => opt.value === actualColorLoaded)) {
                            const newOption = new Option(actualColorLoaded, actualColorLoaded, false, false); 
                            const addNewOption = selectElem.querySelector('option[value="_new_color_"]');
                            if (addNewOption) { selectElem.insertBefore(newOption, addNewOption); } 
                            else { selectElem.add(newOption); }
                        }
                        selectElem.value = actualColorLoaded; 
                        currentColor = actualColorLoaded; 
                        document.getElementById('current_color_display').textContent = actualColorLoaded;
                    })
                    .catch(err => { 
                        console.error(`加载颜色参数 '${colorName}' 失败:`, err); 
                        alert(`加载颜色 '${colorName}' 参数失败: ${err.message}`);
                        const selectElem = document.getElementById('color_select');
                        if (selectElem.value !== currentColor) { selectElem.value = currentColor; }
                    });
            }
            
            function shutdownServer() { 
                 if (confirm("确定要关闭服务器吗？")) {
                    document.getElementById('exit_btn').disabled = true;
                    document.getElementById('exit_btn').textContent = "正在关闭...";
                    fetch('/shutdown_trigger', { method: 'POST' }) 
                        .then(response => response.json())
                        .then(data => { alert(data.message || "服务器关闭请求已发送。"); })
                        .catch(err => {
                            alert('发送关闭请求失败: ' + err);
                            document.getElementById('exit_btn').disabled = false;
                            document.getElementById('exit_btn').textContent = "退出程序";
                        });
                }
            }

            window.onload = function() { 
                const initialColorName = {{ current_color_name_js | tojson }}; 
                console.log("onload - initialColor from template:", initialColorName);
                currentColor = initialColorName || "default"; 

                const colorSelect = document.getElementById('color_select');
                if (!colorSelect) { console.error("onload: color_select not found!"); return; }

                let foundInSelect = Array.from(colorSelect.options).some(opt => opt.value === currentColor && opt.value !== "_new_color_");
                
                if (foundInSelect) {
                    colorSelect.value = currentColor;
                } else {
                    const firstActualOption = Array.from(colorSelect.options).find(opt => opt.value !== "_new_color_");
                    if (firstActualOption) {
                        colorSelect.value = firstActualOption.value;
                        currentColor = firstActualOption.value; 
                    } else { 
                        console.warn("onload: No valid initial color options. currentColor set to empty or default.");
                        currentColor = "default"; 
                        document.getElementById('current_color_display').textContent = "请选择或添加颜色";
                        updateSliderValues(); 
                        return; 
                    }
                }
                document.getElementById('current_color_display').textContent = currentColor;
                console.log(`onload: Attempting to load initial color '${currentColor}'`);
                loadSpecificColor(currentColor); 
            };
            // 粘贴区结束
        </script>
    </head>
    <body>
        <div class="media-column">
            <h1>HSV实时调节器</h1>
            <div class="video-feed-container">
                <img src="{{ url_for('video_feed_route') }}" alt="Video Feed">
            </div>
        </div>
        <div class="controls-column">
            <h2>控制面板</h2>
            <div class="color-management">
                <label for="color_select">选择/添加颜色:</label>
                <select id="color_select" onchange="selectColor()">
                    {% for name in color_names_list %}
                    <option value="{{ name }}">{{ name }}</option>
                    {% endfor %}
                    <option value="_new_color_">--- 添加新颜色 ---</option>
                </select>
                <!-- 删除按钮已被移除 -->
                <p style="font-size:0.9em;">当前编辑: <strong id="current_color_display" style="color:#007bff;">N/A</strong></p>
            </div>

            <div class="slider-group">
                <label class="slider-label">H (色相) 范围:</label>
                <div class="slider-container">
                    <input type="range" id="h_min" min="0" max="179" value="{{ h_min }}" oninput="updateParams()">
                    <span class="value-display" id="h_min_value">{{ h_min }}</span>
                    <span>-</span>
                    <input type="range" id="h_max" min="0" max="179" value="{{ h_max }}" oninput="updateParams()">
                    <span class="value-display" id="h_max_value">{{ h_max }}</span>
                </div>
            </div>
            
            <div class="slider-group">
                <label class="slider-label">S (饱和度) 范围:</label>
                <div class="slider-container">
                    <input type="range" id="s_min" min="0" max="255" value="{{ s_min }}" oninput="updateParams()">
                    <span class="value-display" id="s_min_value">{{ s_min }}</span>
                    <span>-</span>
                    <input type="range" id="s_max" min="0" max="255" value="{{ s_max }}" oninput="updateParams()">
                    <span class="value-display" id="s_max_value">{{ s_max }}</span>
                </div>
            </div>
            
            <div class="slider-group">
                <label class="slider-label">V (明度) 范围:</label>
                <div class="slider-container">
                    <input type="range" id="v_min" min="0" max="255" value="{{ v_min }}" oninput="updateParams()">
                    <span class="value-display" id="v_min_value">{{ v_min }}</span>
                    <span>-</span>
                    <input type="range" id="v_max" min="0" max="255" value="{{ v_max }}" oninput="updateParams()">
                    <span class="value-display" id="v_max_value">{{ v_max }}</span>
                </div>
            </div>
            
            <div class="button-group">
                <button class="button save-btn" onclick="saveAllConfigs()">保存所有配置</button>
                <button class="button exit-btn" id="exit_btn" onclick="shutdownServer()">退出程序</button>
            </div>
        </div>
    </body>
    </html>
    ''',
    h_min=int(lower_val[0]), h_max=int(upper_val[0]), 
    s_min=int(lower_val[1]), s_max=int(upper_val[1]),
    v_min=int(lower_val[2]), v_max=int(upper_val[2]),
    current_color_name_js=active_color_name, 
    color_names_list=color_names_list
    )

# --- Python 路由 ---
@app.route('/video_feed')
def video_feed_route(): 
    return Response(gen_frames(), mimetype='multipart/x-mixed-replace; boundary=frame')

@app.route('/set_params', methods=['POST'])
def set_params_route(): # (与之前相同)
    global hsv_configs, current_color_name; data = request.get_json()
    if not data: return jsonify(success=False, message="无效请求"), 400
    color_to_update = data.get('color_name', current_color_name) 
    with lock:
        if not color_to_update: color_to_update = current_color_name or "default"
        if color_to_update not in hsv_configs: hsv_configs[color_to_update] = {} 
        try:
            h_min,h_max,s_min,s_max,v_min,v_max = (int(data.get(k, d)) for k,d in 
                [('h_min',0),('h_max',179),('s_min',0),('s_max',255),('v_min',0),('v_max',255)])
        except: return jsonify(success=False, message="参数必须为整数"), 400
        hsv_configs[color_to_update]["lower"] = np.array([min(h_min,h_max),min(s_min,s_max),min(v_min,v_max)])
        hsv_configs[color_to_update]["upper"] = np.array([max(h_min,h_max),max(s_min,s_max),max(v_min,v_max)])
        if current_color_name != color_to_update: current_color_name = color_to_update
    return jsonify(success=True, message="参数已更新")

@app.route('/get_color_params/<color_name>', methods=['GET'])
def get_color_params_route(color_name): # (与之前相同，返回 actual_color_loaded)
    global current_color_name, hsv_configs; is_new = request.args.get('is_new','false').lower()=='true'
    with lock:
        actual_loaded_color = color_name
        if color_name in hsv_configs and hsv_configs[color_name] and \
           isinstance(hsv_configs[color_name].get("lower"),np.ndarray) and \
           isinstance(hsv_configs[color_name].get("upper"),np.ndarray):
            current_color_name = color_name; params = hsv_configs[color_name]
        elif is_new:
            hsv_configs[color_name]={"lower":np.array([0,100,100]),"upper":np.array([10,255,255])}
            current_color_name = color_name; params = hsv_configs[color_name]
        else:
            fb_name = current_color_name if current_color_name in hsv_configs and hsv_configs[current_color_name] and isinstance(hsv_configs[current_color_name].get("lower"),np.ndarray) else None
            if not fb_name and hsv_configs: fb_name = next((n for n,c in hsv_configs.items() if c and isinstance(c.get("lower"),np.ndarray)), None)
            if fb_name: current_color_name,actual_loaded_color=fb_name,fb_name; params=hsv_configs[fb_name]; msg=f"'{color_name}'未找到,切换到'{fb_name}'."
            else: _initialize_default_config(); current_color_name=list(hsv_configs.keys())[0]; actual_loaded_color=current_color_name; params=hsv_configs[current_color_name]; msg=f"'{color_name}'未找到,重置到'{current_color_name}'."
            return jsonify({"lower":params["lower"].tolist(),"upper":params["upper"].tolist(),"message":msg,"actual_color_loaded":actual_loaded_color})
        return jsonify({"lower":params["lower"].tolist(),"upper":params["upper"].tolist(),"actual_color_loaded":actual_loaded_color})

# --- 删除 /delete_color_config 路由 ---
# (该路由及其调用函数 delete_color_config_route 已被移除)

@app.route('/save_config_trigger', methods=['GET'])
def save_config_trigger_route(): # (与之前相同)
    save_config(); return jsonify(success=True, message="配置已保存")

def actual_shutdown_server():  # (与之前相同)
    global shutdown_server_attempted
    if shutdown_server_attempted: return
    shutdown_event.set(); 
    if camera: camera.release(); 
    shutdown_server_attempted = True
    try: os.kill(os.getpid(), signal.SIGINT)
    except Exception as e: print(f"发送 SIGINT 失败: {e}")

@app.route('/shutdown_trigger', methods=['POST'])
def shutdown_trigger_route():  # (与之前相同)
    threading.Thread(target=actual_shutdown_server).start()
    return jsonify(message="服务器关闭请求已发送。"), 202

def open_browser_auto(): # (与之前相同)
    def _open(): time.sleep(1.5); webbrowser.open_new_tab("http://127.0.0.1:5000/")
    threading.Thread(target=_open, daemon=True).start()

if __name__ == '__main__': # (与之前相同)
    config_dir = os.path.dirname(CONFIG_FILE)
    if config_dir and not os.path.exists(config_dir):
        try: os.makedirs(config_dir)
        except OSError as e: print(f"创建目录 {config_dir} 失败: {e}")
    open_browser_auto()
    print("Flask 服务器启动 http://127.0.0.1:5000/ CTRL+C退出")
    try: app.run(host='0.0.0.0', port=5000, threaded=True, use_reloader=False) 
    except KeyboardInterrupt: print("KeyboardInterrupt 收到，服务器停止...")
    finally:
        if not shutdown_event.is_set(): shutdown_event.set() 
        if camera and camera.running: camera.release()
        print("主线程退出。")